package com.datang.litejvm.engin;

import com.datang.litejvm.clz.Method;
import com.datang.litejvm.cmd.ByteCodeCommand;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @author: 顶风少年
 * @Description: 函数栈帧
 * @date: 15:41 2022/6/16
 **/
public class StackFrame {
    //局部变量表
    private List<JavaObject> localVariableTable = new ArrayList<JavaObject>();
    //操作数栈
    private Stack<JavaObject> operandStack = new Stack<JavaObject>();

    //字节码指令偏移量,指向下一个操作指令
    int index = 0;

    //当前方法
    private Method m = null;

    //上一个函数栈帧
    private StackFrame callerFrame = null;

    private StackFrame(Method m) {
        this.m = m;
    }

    //创建函数栈帧
    public static StackFrame create(Method m) {
        StackFrame frame = new StackFrame(m);
        return frame;
    }

    /**
     * @author: 顶风少年
     * @Description: 上一个函数栈帧
     * @date: 16:44 2022/6/16
     **/
    public StackFrame getCallerFrame() {
        return callerFrame;
    }

    public void setCallerFrame(StackFrame callerFrame) {
        this.callerFrame = callerFrame;
    }

    /**
     * @author: 顶风少年
     * @Description: 栈帧所属方法
     * @date: 16:45 2022/6/16
     **/
    public Method getMethod() {
        return m;
    }

    /**
     * @author: 顶风少年
     * @Description: 设置局部变量表
     * @date: 16:40 2022/6/16
     **/
    public void setLocalVariableTable(List<JavaObject> values) {
        this.localVariableTable = values;
    }

    /**
     * @author: 顶风少年
     * @Description: 获取局部变量表中的某个变量
     * @date: 10:22 2022/6/17
     **/
    public JavaObject getLocalVariableValue(int index) {
        return this.localVariableTable.get(index);
    }

    /**
     * @author: 顶风少年
     * @Description: 向局部变量表设置值
     * @date: 10:38 2022/6/17
     **/
    public void setLocalVariableValue(int index, JavaObject jo) {
        //问题： 为什么要这么做？？
        if (this.localVariableTable.size() - 1 < index) {
            for (int i = this.localVariableTable.size(); i <= index; i++) {
                this.localVariableTable.add(null);
            }
        }
        this.localVariableTable.set(index, jo);
    }


    /**
     * @author: 顶风少年
     * @Description: 执行方法, 就是执行字节码指令
     * @date: 17:11 2022/6/16
     **/
    public ExecutionResult execute() {
        List<ByteCodeCommand> cmds = m.getCodeAttr().getCmds();
        while (index < cmds.size()) {
            //执行结果
            ExecutionResult result = new ExecutionResult();
            //下一条字节码指令
            ByteCodeCommand cmd = cmds.get(index);
            System.out.println(cmd.toString());
            //执行
            cmd.execute(this, result);
            //运行下一条
            if (result.isRunNextCmd()) {
                index++;
            } else if (result.isExitCurrentFrame()) {
                //退出当前栈帧,return 剩余的栈帧不执行了
                return result;
            } else if (result.isPauseAndRunNewFrame()) {
                //暂停当前栈帧,执行新的函数栈帧
                index++;
                return result;
            } else if (result.isJump()) {
                //跳转指令,跳转到下一个字节码指令
                int offset = result.getNextCmdOffset();
                //设置下一个指令的偏移量
                this.index = getNextCommandIndex(offset);
            } else {
                index++;
            }
        }
        //如果循环走完了,说明没有任何的跳转,停止,表示当前StackFrame的指令全部执行完毕,可以退出了
        ExecutionResult result = new ExecutionResult();
        result.setNextAction(ExecutionResult.EXIT_CURRENT_FRAME);
        return result;
    }

    /**
     * @author: 顶风少年
     * @Description: 根据偏移量查询字节码指令
     * @date: 17:48 2022/6/16
     **/
    public int getNextCommandIndex(int offset) {
        List<ByteCodeCommand> cmds = m.getCodeAttr().getCmds();
        for (int i = 0; i < cmds.size(); i++) {
            if (cmds.get(i).getOffset() == offset) {
                return i;
            }
        }
        throw new RuntimeException("Can't find next command");
    }

    /**
     * @author: 顶风少年
     * @Description: 获取操作数栈
     * @date: 19:04 2022/6/16
     **/
    public Stack<JavaObject> getOperandStack() {
        return this.operandStack;
    }







}
